#include "schedulepage.h"
#include "ui_schedulepage.h"
#include <QMenu>
#define random(a,b) (rand() % (b-a+1))+a

SchedulePage::SchedulePage(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::SchedulePage),
    timer(new QTimer(this))
{
    ui->setupUi(this);
    backupque = new backupQue();
    addwork = new addWork();
    memoryUsage = new MemoryUsage();
    init();

    connect(addwork, SIGNAL(sendData(PCB*)), this, SLOT(receiveData(PCB*)));

}

SchedulePage::~SchedulePage()
{
    if(backupque!=nullptr) delete backupque;
    if(addwork!=nullptr) delete addwork;
    if(memoryUsage!=nullptr) delete memoryUsage;
    delete ui;
}

void SchedulePage::init()
{
//    processTableModel = new QStandardItemModel(this);
//     /* 设置表格标题行(输入数据为QStringList类型) */
//    processTableModel->setHorizontalHeaderLabels(QStringList()<<"pid"<<"runTime"<<"priority"<<"status");
//    //不可编辑
//    ui->processTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
//    //设置model
//    ui->processTable->setModel(processTableModel);
//    //去掉自动序号列
//    //ui->readyQueueTable->verticalHeader()->setHidden(true);
//    // 表格宽度自动根据UI进行计算，不可手动调整宽度
//    ui->processTable->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
//    //显示网格线
//    ui->processTable->setShowGrid(true);

    this->setWindowTitle("CPU调度模拟");
    //设置窗口大小不可改变
    //this->setFixedSize(this->width(), this->height());
    this->setWindowFlags(windowFlags()&~Qt::MaximizeUsingFullscreenGeometryHint);


//    //自适应大小  cpu图片
//    ui->cpu_label->setScaledContents(true);
//    ui->cpu_label->setPixmap(QPixmap("://images/OIP-C.jfif"));

    ui->bg->setScaledContents(true);
    ui->bg->setPixmap(QPixmap("://images/bg.jpg"));

    //selectAlogrithm选择算法按钮
    alogrithm = 0; //默认 0

    addrAssign = 0;//初始默认为最先适应算法
    ui->algorithm_label->setText("抢占式动态优先权");

    num_in_memory = 0;
    num_in_suspend = 0;
    num_in_backup = 0;
    num_unlock = 0;
    srand((unsigned)time(NULL));//PID随机数种子init
    //初始化调用一次随机生成函数，生成5个进程
    randomProcess();

    curRunTime = 0;

    curmemorypercent = 0;//初始内存占比为0
    cpuWait = false;//cpu处于未进入等待状态
    ifCpuWaitOver = true;//一开始cpu处于等待完毕状态，cpu对move的进程的时间--不会发生
    ifCpuSchedule = false;//CPU调度尚未开始
    ifCpuScheduleIsRunning = false;
    running = nullptr;
    ui->compaction->setVisible(false);


    initReadyQue();//初始化就绪队列
    initSuspend();//初始化挂起队列
    initMemoryPhoto();//初始化内存占用显示图
    initMemoryTable();//初始化内存展示表
    initMemory();//初始化代表内存的数组
    initUnpartiTable();//初始化未分分区表

    //不可编辑
    ui->readyQue->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->suspendque->setEditTriggers(QAbstractItemView::NoEditTriggers);

    //创建算法选择菜单
    creatSelectAlogrithmMenu();
}

void SchedulePage::initReadyQue()//初始化就绪队列
{
    ui->readyQue->setContextMenuPolicy(Qt::CustomContextMenu);//增加右键菜单功能的控件
    ui->readyQue->setSelectionBehavior(QAbstractItemView::SelectRows);//设置整行选择
    ui->readyQue->setSelectionMode(QAbstractItemView::SingleSelection);//设置只选择一行
    num_in_memory = 0;
    for(int i =0; i < NUM_OF_PROCESS; ++i)
    {
        ready[i] = nullptr;
    }
    updateReadyQueData();
}
void SchedulePage::initSuspend()//初始化挂起队列
{
    ui->suspendque->setContextMenuPolicy(Qt::CustomContextMenu);//增加右键菜单功能的控件
    ui->suspendque->setSelectionBehavior(QAbstractItemView::SelectRows);//设置整行选择
    ui->suspendque->setSelectionMode(QAbstractItemView::SingleSelection);//设置只选择一行
    for(int i = 0; i < NUM_OF_PROCESS; ++i){
        suspend[i] = nullptr;
        unlock[i] = nullptr;
    }
}

/**
 * @brief SchedulePage::initMemory
 * 初始化代表内存的数组
 */
void SchedulePage::initMemory()
{
    for(int i=0;i<MEMORYSIZE;i++)
        memory[i] = 0;

    for(int i=0;i<OSMEMORY;i++)
        memory[i] = 1;
}

void SchedulePage::initMemoryTable()
{
    ui->memoryTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->memoryTable->setRowCount(MEMORYSIZE);
    for(int i=0;i<OSMEMORY;i++){
        if(i==0)
            ui->memoryTable->setItem(i,0,new QTableWidgetItem(QString("OS")));
        ui->memoryTable->setItem(i,1,new QTableWidgetItem(QString::number(i)));
        ui->memoryTable->setItem(i,2,new QTableWidgetItem(QString("占用")));
        ui->memoryTable->item(i,1)->setBackgroundColor(QColor("#A0FEFB"));
        ui->memoryTable->item(i,2)->setBackgroundColor(QColor("#A0FEFB"));
    }
}

/**
 * 初始化内存占用显示图
 */
void SchedulePage::initMemoryPhoto()
{
    memoryUsage = new MemoryUsage(this);
    memoryUsage->setGeometry(470,0,280,280);
    memoryUsage->updateValue(OSMEMORY);
    memoryUsage->show();
}

/**
 * @brief SchedulePage::initProcsInMemory
 * 更新inmemory表（在内存中的进程的指针的集合）
 */
void SchedulePage::initProcsInMemory()
{
    inmemory.clear();
    for(int i=0;i<num_in_memory;i++){
            PCB *p = ready[i];
            inmemory.append(p);
    }
    if(running != nullptr)
        inmemory.append(running);
}

void SchedulePage::initUnpartiTable()//初始化未分分区表
{
    ui->unpartitable->setEditTriggers(QAbstractItemView::NoEditTriggers);
    UnpartitionTable *item = new UnpartitionTable(OSMEMORY, MEMORYSIZE-OSMEMORY, NOTPARTIED);
    unpartitionTable.append(item);
    int rowCount = ui->unpartitable->rowCount();
    ui->unpartitable->insertRow(rowCount);
    ui->unpartitable->setItem(rowCount, 0, new QTableWidgetItem(QString::number(item->beginAddr)));
    ui->unpartitable->setItem(rowCount, 1, new QTableWidgetItem(QString::number(item->width)));
    ui->unpartitable->setItem(rowCount, 2, new QTableWidgetItem(QString(item->tableStatus==0?"Empty":"Not Empty")));
}

void SchedulePage::memoryProcsUsedSort()//将在内存中的进程按照地址排序
{
     qSort(inmemory.begin(), inmemory.end(), cmpOfProcsUsedSort);
}

bool SchedulePage::cmpOfProcsUsedSort(const PCB *info1, const PCB *info2)
{
    return info1->getMemoryBegin() < info2->getMemoryBegin();
}

/**
 * @brief SchedulePage::refreshMemoryTable
 * 根据进程pcb信息刷新内存展示表格
 */
void SchedulePage::refreshMemoryTable()
{
    ui->memoryTable->clearContents();
    ui->memoryTable->setRowCount(0);
    initProcsInMemory();
    initMemoryTable();

    for(int i=0;i<inmemory.size();i++){
        PCB * p = inmemory.at(i);
        //将这个进程的信息更新到内存区域展示表格中
        int beginAddr = p->getMemoryBegin();
        int width = p->getMemoryNeed();
        int colorOrder = p->getColorOrder();
        QColor color = QColor(colorOrder==0?"#FBF89A":"#B0EACC");
    //            qDebug()<<width;
        /* 处理第一行（因为左边有要附加PID信息） */
        ui->memoryTable->setItem(beginAddr, 0, new QTableWidgetItem(p->getPID()));
        ui->memoryTable->setItem(beginAddr, 1, new QTableWidgetItem(QString("0")));
        ui->memoryTable->setItem(beginAddr, 2, new QTableWidgetItem(QString("占用")));
        ui->memoryTable->item(beginAddr,1)->setBackgroundColor(color);
        ui->memoryTable->item(beginAddr,2)->setBackgroundColor(color);
        /* 处理第一条之后的所有条 */
        for(int j=beginAddr+1, k=1;j<beginAddr+width;j++,k++){
            ui->memoryTable->setItem(j, 1, new QTableWidgetItem(QString::number(k)));
            ui->memoryTable->setItem(j, 2, new QTableWidgetItem(QString("占用")));
            ui->memoryTable->item(j,1)->setBackgroundColor(QColor(color));
            ui->memoryTable->item(j,2)->setBackgroundColor(QColor(color));
        }
    }
}

void SchedulePage::refreshProcsColors()//更新所有进程（不包括即将要进入的进程）的颜色状态
{
    for(int i=0;i<inmemory.size();i++){
            inmemory[i]->refreshColor();
    }
}

/**
 * 将进程按优先级进入ready队列）
 */
void SchedulePage::addProcessByPrio(PCB * p)
{
    //qDebug()<<"正在从后备队列中选取进程";
    if(num_in_memory < NUM_OF_PROCESS)//就绪队列有位置
    {
        PCB * pro = new PCB(p);//深拷贝
        bool ok = true;
        int i;
        for(i = 0; i < num_in_memory; i++)
        {
            if(p->getPriority() > ready[i]->getPriority())
            {
                for(int j = num_in_memory; j > i; j--)
                {
                    ready[j] = ready[j - 1];
                }
                ok = true;
                break;
            }
        }
        if(ok){
            ready[i] = p;
        }
        else{
            ready[num_in_memory] = p;
        }
        num_in_memory++;
        updateReadyQueData();
    }
}

/**
 * 将进程按到达顺序进入ready队列）
 */
void SchedulePage::addProcessByFC(PCB * p)
{
    if(num_in_memory < NUM_OF_PROCESS)//就绪队列有位置
    {
        PCB * pro = new PCB(p);//深拷贝
        ready[num_in_memory] = pro;
        num_in_memory++;
        updateReadyQueData();
    }
}

/**
 * @brief SchedulePage::updateReadyQueData
 * 将进程按运行时间从小到大进入ready队列
 */
void SchedulePage::addProcessByRunt(PCB * p)
{
    if(num_in_memory < NUM_OF_PROCESS)//就绪队列有位置
    {
        PCB * pro = new PCB(p);//深拷贝
        bool ok = true;
        int i;
        for(i = 0; i < num_in_memory; i++)
        {
            if(pro->getRuntime() < ready[i]->getRuntime())
            {
                for(int j = num_in_memory; j > i; j--)
                {
                    ready[j] = ready[j - 1];
                }
                ok = true;
                break;
            }
        }
        if(ok){
            ready[i] = p;
        }
        else{
            ready[num_in_memory] = p;
        }
        num_in_memory++;
        updateReadyQueData();

    }
}
void SchedulePage::updateReadyQueData()
{
//    QStandardItem * pid_item = new QStandardItem(QString::number(8080));
//    QStandardItem * runTime_item = new QStandardItem(QString::number(5));
//    QStandardItem * priority_item = new QStandardItem(QString::number(11));
//    QStandardItem * status_item = new QStandardItem("running");
//    processTableModel->setItem(0,0,pid_item);
//    processTableModel->setItem(0,1,runTime_item);
//    processTableModel->setItem(0,2,priority_item);
//    processTableModel->setItem(0,3,status_item);

    refreshMemoryTable();//刷新内存占用的展示表格

    ui->readyQue->setRowCount(0);
    ui->readyQue->clearContents();
    for(int i = 0; i < num_in_memory; i++)
    {
        PCB * p = ready[i];
        int rowcount = ui->readyQue->rowCount();
        ui->readyQue->insertRow(rowcount);//增加一行
        ui->readyQue->setItem(rowcount, 0, new QTableWidgetItem(p->getPID()));
        ui->readyQue->setItem(rowcount, 1, new QTableWidgetItem(QString::number(p->getPriority())));
        ui->readyQue->setItem(rowcount, 2, new QTableWidgetItem(QString::number(p->getRuntime())));
        ui->readyQue->setItem(rowcount, 3, new QTableWidgetItem("ready"));
    }
//    for(int i = 0 ; i < num_in_memory; ++i)
//    {
//        pri = QString::number(ready[i]->getPriority());
//        tim = QString::number(ready[i]->getRuntime());
//        status = ready[i]->getStatus()==READY?"就绪":"运行中";
//        ui->readyQue->setItem(i,0,new QTableWidgetItem(ready[i]->getPID()));
//        ui->readyQue->setItem(i,1,new QTableWidgetItem(pri));
//        ui->readyQue->setItem(i,2,new QTableWidgetItem(tim));
//        ui->readyQue->setItem(i,3,new QTableWidgetItem(status));
//    }
}

void SchedulePage::updateSusbendData()
{
    ui->suspendque->setRowCount(0);
    ui->suspendque->clearContents();
    for(int i = 0; i < num_in_suspend; i++)
    {
        PCB * p = suspend[i];
        int rowcount = ui->suspendque->rowCount();
        ui->suspendque->insertRow(rowcount);//增加一行
        ui->suspendque->setItem(rowcount, 0, new QTableWidgetItem(p->getPID()));
        ui->suspendque->setItem(rowcount, 1, new QTableWidgetItem(QString::number(p->getPriority())));
        ui->suspendque->setItem(rowcount, 2, new QTableWidgetItem(QString::number(p->getRuntime())));
    }
}

void SchedulePage::upDateMemoryPhoto()//更新内存盘的展示效果
{
    timerMemoryPhoto = startTimer(60);
}

/**
 * @brief SchedulePage::refreshUnpartiTable
 * 刷新未分分区表展示表格
 */
void SchedulePage::refreshUnpartiTable()
{
    ui->unpartitable->clearContents();
    ui->unpartitable->setRowCount(0);
    if(unpartitionTable.size()==0)
        return;
    foreach (auto item, unpartitionTable) {
        int rowCount = ui->unpartitable->rowCount();
        ui->unpartitable->insertRow(rowCount);
        ui->unpartitable->setItem(rowCount, 0, new QTableWidgetItem(QString::number(item->beginAddr)));
        ui->unpartitable->setItem(rowCount, 1, new QTableWidgetItem(QString::number(item->width)));
        ui->unpartitable->setItem(rowCount, 2, new QTableWidgetItem(QString(item->tableStatus==0?"Empty":"Not Empty")));
    }
}

void SchedulePage::receiveData(PCB * p)//接受addwork界面传过来的新pcb添加到后备队列
{
    qDebug() << "进程添加成功";
    PCB * pro = new PCB(p);
    if(backupque->addBackup(pro)){
        num_in_backup++;
    }
}

void SchedulePage::on_startButton_clicked()
{
    ifCpuSchedule = true;
    ifCpuScheduleIsRunning = true;
    initReadyQue();//为了避免 中途切换调度算法导致混乱， 清空ready队列
    initTimers();
}

void SchedulePage::timerEvent(QTimerEvent *eve)
{
    if(eve->timerId() == unit_time && alogrithm == 0)//抢占式动态优先级
    {
        cpuScheduleByPrio();
    }
    else if(eve->timerId() == time_slice && alogrithm == 0)
    {
        ifOccurQzByPrio();
    }
    else if(eve->timerId() == unit_time && alogrithm == 1)//非抢占式优先级
    {
        cpuScheduleByPrio();
    }
    else if(eve->timerId() == unit_time && alogrithm == 2)//纯时间片
    {
        RRSchedule();
    }
    else if(eve->timerId() == time_slice && alogrithm == 2)
    {
        ifFinishInSlice();//时间片到了  进程是否完成
    }
    else if(eve->timerId() == unit_time && alogrithm == 3)//FCFS
    {
        FCFSSchedule();
    }
    else if(eve->timerId() == unit_time && alogrithm == 4)//非抢占式短作业优先
    {
        SJFSchedule();
    }
    else if(eve->timerId() == unit_time && alogrithm == 5)//抢占式短作业优先
    {
        SJFSchedule();
    }
    else if(eve->timerId() == time_slice && alogrithm == 5)
    {
        ifOccurQzByRunt();
    }
    else if (eve->timerId()==timerMemoryPhoto) {
        if(memoryUsage->getValue()<curmemorypercent)
            memoryUsage->addValue();
        else if (memoryUsage->getValue()>curmemorypercent)
            memoryUsage->delValue();
        else
            this->killTimer(timerMemoryPhoto);
    }
}

void SchedulePage::cpuScheduleByPrio()
{
    //被解挂的进程优先进就绪队列
    if( num_in_memory < NUM_OF_PROCESS && num_unlock > 0)
    {
        int i = num_unlock - 1;
        PCB * p = new PCB(unlock[i]);
        if(ifCanAddToMemory(p))
        {
            addProcessByPrio(p);
            num_unlock -= 1;
            delete unlock[i];
            if(unlock[i] != nullptr)
            {
                unlock[i] = nullptr;
            }
        }
    }
    else if(num_in_memory < NUM_OF_PROCESS && num_in_backup > 0)
    {
        PCB * p = new PCB(backupque->getHead());
        if(ifCanAddToMemory(p))//是否内存分配也成功，新加进主界面的ready队列
        {
            backupque->delFirst();
            num_in_backup--;
            addProcessByPrio(p);
        }else {
            qDebug()<< "内存不足，分配失败";
        }
    }
    if(running == nullptr)
    {
        running = popReadyProcs();
    }

    if(num_in_memory == 0 && num_unlock == 0 && running == nullptr)
    {
        on_terminateButton_clicked();
        QMessageBox::information(nullptr, "over", "运行结束");
        ifCpuSchedule = false;
        updateCpuStatus();
        exitMemoryMerge();//合并函数，进程运行完后，将未分分区合并
        return;
    }

    running->delRuntime(1);//时间数减1
    running->delPriority(1);//优先级减1
    if(running->getRuntime() == 0)
    {
        exitMemoryRelease(running);
        exitMemoryMerge();
        delete running;//释放掉运行时间为0的进程
        running = nullptr;
    }
    updateCpuStatus();

    refreshMemoryTable();//刷新内存占用的展示表格
    getCurMemoryPercent();
    upDateMemoryPhoto();//刷新圆盘
}

void SchedulePage::ifOccurQzByPrio()
{
    if(running == nullptr || num_in_memory == 0) return;
    if(running->getPriority() < ready[0]->getPriority())//经过一个时间片，如果就绪队列中存在优先级比正在运行的进程高的，发生抢占
    {
        PCB * p = new PCB(running);
        delete running;
        running = popReadyProcs();
        running->setStatus(RUNNING);

        bool ok = true;
        int i;
        for(i = 0; i < num_in_memory; i++)
        {
            if(p->getPriority() > ready[i]->getPriority())
            {
                for(int j = num_in_memory; j > i; j--)
                {
                    ready[j] = ready[j - 1];
                }
                ok = true;
                break;
            }
        }
        if(ok){
            ready[i] = p;
        }
        else{
            ready[num_in_memory] = p;
        }
        num_in_memory++;
        updateReadyQueData();
    }
}

void SchedulePage::RRSchedule()
{
    //时间片轮转基于 FCFS
    FCFSSchedule();
}

void SchedulePage::ifFinishInSlice()
{
    if(running == nullptr || num_in_memory == 0) return;
    if(running->getPriority() > 0)//经过一个时间片，轮转
    {
        PCB * p = new PCB(running);
        delete running;
        running = popReadyProcs();
        running->setStatus(RUNNING);

        addProcessByFC(p); //将在一个时间片内未运行完的进程替换下来 到就绪队列尾部
        updateReadyQueData();
    }
}

bool SchedulePage::ifCanAddToMemory(PCB * p)//是否内存分配也成功，新加进主界面的ready队列
{
        if(addrAssignment(p) == 1){
            //内存分配成功
            return true;
        }else{
            return false;
        }
}

bool SchedulePage::isEveryEnough(int procsWidth)//检测函数，是否有未分分区足够
{
    for(int i=0;i<unpartitionTable.size();i++){
        if(unpartitionTable.at(i)->width>=procsWidth){
            return true;
        }
    }
    return false;
}

bool SchedulePage::isAllEnough(int procsWidth)//未分分区总和是否足够
{
    if(sumOfUnptTableSpace()>=procsWidth)
        return true;
    else
        return false;
}

/**
 * @brief SchedulePage::sumOfUnptTableSort
 * @return Sum
 * 求和函数，求余下所有碎片的总剩余内存
 */
int SchedulePage::sumOfUnptTableSpace(){
    int result = 0;
    foreach (auto item, unpartitionTable) {
        result += item->width;
    }
    return result;
}

int SchedulePage::addrAssignment(PCB *targetProcs)//内存分配，分配成功返回1，否则返回0
{
    if(isEveryEnough(targetProcs->getMemoryNeed())){//有独立的分区可以装的下
        int index = -1;
        if(addrAssign==0){
            index = getIndexByFirst(targetProcs->getMemoryNeed());//分配下标
            if(index==-1)
                return 0;
            }
        else if(addrAssign==1){
            index = getIndexByBest(targetProcs->getMemoryNeed());
            if(index==-1)
                return 0;
        }
        else if(addrAssign==2){
            index = getIndexByWorst(targetProcs->getMemoryNeed());
            if(index==-1)
                return 0;
        }

        int width = unpartitionTable.at(index)->width;
        int need = targetProcs->getMemoryNeed();
        if(width >= need){//。。。条件成立,存在某分区大于请求的内存，为该进程分配内存起址(初值为-1)
            /*未分分区表的单元项保存的是显示的表格中的编号，是从1开始的，但是实际计算应该从0开始*/
            targetProcs->setMemoryBegin(unpartitionTable.at(index)->beginAddr-1);
            if(width==need){//表示该分区已经被全部分配完毕，可以删除该记录
                UnpartitionTable *t = unpartitionTable.at(index);
                unpartitionTable.removeAt(index);
                delete t;
                t = NULL;
            }
            else {
                unpartitionTable.at(index)->beginAddr += need;//起址下移
                unpartitionTable.at(index)->width -= need;
            }
            refreshUnpartiTable();//刷新未分分区表的显示表格
            int begin = targetProcs->getMemoryBegin();
            for(int j=begin;j<begin+need;j++){//设置代表内存的数组
                memory[j] = 1;
            }
            return 1;//分配成功
        }
        else
            return 0;
    }
    else if ( isAllEnough(targetProcs->getMemoryNeed()) ) {
        memoryCompaction();
        int width = unpartitionTable.at(0)->width;
        int need = targetProcs->getMemoryNeed();
        targetProcs->setMemoryBegin(unpartitionTable.at(0)->beginAddr - 1);
        if(width==need){//表示该分区已经被全部分配完毕，可以删除该记录
            UnpartitionTable *t = unpartitionTable.at(0);
            unpartitionTable.removeAt(0);
            delete t;
            t = NULL;
        }
        else {
            unpartitionTable.at(0)->beginAddr += need;//起址下移
            unpartitionTable.at(0)->width -= need;
        }
        refreshUnpartiTable();//刷新未分分区表的显示表格
        int begin = targetProcs->getMemoryBegin();
        for(int j=begin;j<begin+need;j++){//设置代表内存的数组
            memory[j] = 1;
        }
        return 1;
    }
    else{//条件不成立视为内存不足
        qDebug()<<"memory space is not enough when addrAssignment()";
        return 0;
    }
}

void SchedulePage::memoryCompaction()//内存紧缩,检测到未分分区的大小都不足以放下新的进程，但是总的剩余内存足以存下新的进程，发生内存紧缩
{
    ui->compaction->setVisible(true);
    initProcsInMemory();
    memoryProcsUsedSort();//将进程按照起址从小到大排序
    int size = inmemory.size();
    int nextBegin = OSMEMORY;
    for(int i=0;i<size;i++){//将所有在内存的进程移动一遍进行紧缩，主要是改变PCB信息
        if(i>=inmemory.size())   break;
        inmemory.at(i)->setMemoryBegin(nextBegin);
        int beginAddr = inmemory.at(i)->getMemoryBegin();
        int width = inmemory.at(i)->getMemoryNeed();
        nextBegin = beginAddr + width;
    }
    int space = sumOfUnptTableSpace();
    int unptBegin = MEMORYSIZE - space;
    qDeleteAll(unpartitionTable);
    unpartitionTable.clear();
    unpartitionTable.append( new UnpartitionTable(unptBegin, space, NOTPARTIED) );

    refreshMemoryTable();
    refreshUnpartiTable();
}

void SchedulePage::FCFSSchedule()
{
    //被解挂的进程优先进就绪队列
    if( num_in_memory < NUM_OF_PROCESS && num_unlock > 0)
    {
        int i = num_unlock - 1;
        PCB * p = new PCB(unlock[i]);
        if(ifCanAddToMemory(p)){
            addProcessByFC(p);//按到达顺序添加入就绪队列

            num_unlock -= 1;
            delete unlock[i];
            if(unlock[i] != nullptr)
            {
                unlock[i] = nullptr;
            }
        }
    }
    else if(num_in_memory < NUM_OF_PROCESS && num_in_backup > 0)
    {
        PCB * p = new PCB(backupque->getHead());
        if(ifCanAddToMemory(p)){
            backupque->delFirst();
            num_in_backup--;
            addProcessByFC(p);
        }
    }
    if(running == nullptr)
    {
        running = popReadyProcs();
    }

    if(num_in_memory == 0 && num_unlock == 0 && running == nullptr)
    {
        on_terminateButton_clicked();
        QMessageBox::information(nullptr, "over", "运行结束");
        ifCpuSchedule = false;
        updateCpuStatus();
        exitMemoryMerge();//合并函数，进程运行完后，将未分分区合并
        return;
    }

    running->delRuntime(1);//时间数减1
    //running->delPriority(1);//优先级减1
    if(running->getRuntime() == 0)
    {
        exitMemoryRelease(running);
        exitMemoryMerge();
        delete running;//释放掉运行时间为0的进程
        running = nullptr;
    }
    updateCpuStatus();

    refreshMemoryTable();//刷新内存占用的展示表格
    getCurMemoryPercent();
    upDateMemoryPhoto();//刷新圆盘
}

void SchedulePage::SJFSchedule()
{
    //被解挂的进程优先进就绪队列
        if( num_in_memory < NUM_OF_PROCESS && num_unlock > 0)
        {
            int i = num_unlock - 1;
            PCB * p = new PCB(unlock[i]);
            if(ifCanAddToMemory(p)){
                addProcessByRunt(p);

                num_unlock -= 1;
                delete unlock[i];
                if(unlock[i] != nullptr)
                {
                    unlock[i] = nullptr;
                }
            }

        }
        else if(num_in_memory < NUM_OF_PROCESS && num_in_backup > 0)
        {
            PCB * p = new PCB(backupque->getHead());
            if(ifCanAddToMemory(p)){
                num_in_backup--;
                backupque->delFirst();
                addProcessByRunt(p);
            }

        }
        if(running == nullptr && num_in_memory>0)
        {
            running = popReadyProcs();
        }

        if(num_in_memory == 0 && num_unlock == 0 && running == nullptr)
        {
            on_terminateButton_clicked();
            QMessageBox::information(nullptr, "over", "运行结束");
            ifCpuSchedule = false;
            updateCpuStatus();
            exitMemoryMerge();//合并函数，进程运行完后，将未分分区合并
            return;
        }

        running->delRuntime(1);//时间数减1

        if(running->getRuntime() == 0)
        {
            exitMemoryRelease(running);
            exitMemoryMerge();
            delete running;//释放掉运行时间为0的进程
            running = nullptr;
        }
        updateCpuStatus();

        refreshMemoryTable();//刷新内存占用的展示表格
        getCurMemoryPercent();
        upDateMemoryPhoto();//刷新圆盘
}

void SchedulePage::ifOccurQzByRunt()
{
    if(running == nullptr || num_in_memory == 0) return;
    if(running->getRuntime() > ready[0]->getRuntime())//经过一个时间片，如果就绪队列中存在剩余时间比正在运行的进程短的，发生抢占
    {
        PCB * p = new PCB(running);
        delete running;
        running = popReadyProcs();
        running->setStatus(RUNNING);

        bool ok = true;
        int i;
        for(i = 0; i < num_in_memory; i++)
        {
            if(p->getRuntime() < ready[i]->getRuntime())
            {
                for(int j = num_in_memory; j > i; j--)
                {
                    ready[j] = ready[j - 1];
                }
                ok = true;
                break;
            }
        }
        if(ok){
            ready[i] = p;
        }
        else{
            ready[num_in_memory] = p;
        }
        num_in_memory++;
        updateReadyQueData();
    }
}

void SchedulePage::creatSelectAlogrithmMenu()//算法选择菜单
{
    QMenu * menu = new QMenu(this);
    QAction * action1 = new QAction(this);
    QAction * action2 = new QAction(this);
    QAction * action3 = new QAction(this);
    QAction * action4 = new QAction(this);
    QAction * action5 = new QAction(this);
    QAction * action6 = new QAction(this);

    action1->setText("抢占式动态优先权");
    action2->setText("非抢占式优先权");
    action3->setText("纯时间片轮转");
    action4->setText("先到先服务");
    action5->setText("非抢占式最短作业优先");
    action6->setText("抢占式最短作业优先");

    connect(action1, &QAction::triggered, [=]{
        alogrithm = 0;
        qDebug()<< alogrithm << "抢占式动态优先级";
        ui->algorithm_label->setText("抢占式动态优先级");
    });

    connect(action2, &QAction::triggered, [=]{
        alogrithm = 1;
        qDebug()<< alogrithm << "非抢占式优先权";
          ui->algorithm_label->setText("非抢占式优先权");
    });

    connect(action3, &QAction::triggered, [=]{
        alogrithm = 2;
        qDebug()<< alogrithm << "纯时间片轮转";
                  ui->algorithm_label->setText("纯时间片轮转");
    });

    connect(action4, &QAction::triggered, [=]{
        alogrithm = 3;
        qDebug()<< alogrithm << "先到先服务";
                  ui->algorithm_label->setText("先到先服务");
    });

    connect(action5, &QAction::triggered, [=]{
        alogrithm = 4;
        qDebug()<< alogrithm << "非抢占式最短作业优先";
                  ui->algorithm_label->setText("非抢占式最短作业优先");
    });

    connect(action6, &QAction::triggered, [=]{
        alogrithm = 5;
        qDebug()<< alogrithm << "抢占式最短作业优先";
                  ui->algorithm_label->setText("抢占式最短作业优先");
    });

    menu->addAction(action1);
    menu->addAction(action2);
    menu->addAction(action3);
    menu->addAction(action4);
    menu->addAction(action5);
    menu->addAction(action6);

    ui->selectAlogrithmMenu->setPopupMode(QToolButton::InstantPopup);
    ui->selectAlogrithmMenu->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    ui->selectAlogrithmMenu->setMenu(menu);//设置下拉菜单
}

void SchedulePage::updateCpuStatus()
{
    ui->compaction->setVisible(false);
    if(ifCpuSchedule)
    {
        if(running != nullptr)
        {
           ui->runningPro->setText("PID:" + running->getPID() + " priority:" + QString::number(running->getPriority())
                                   + " runtime:" + QString::number(running->getRuntime()));
        }
        if(ifCpuScheduleIsRunning)
                   ui->cpu_status->setText("调度中    running");
        else {
                   ui->cpu_status->setText("调度中    wating");
        }
    }
    else {
        ui->cpu_status->setText("未调度");
        ui->runningPro->setText("");
    }
}

void SchedulePage::randomProcess()//随机生成5个进程
{
    for(int i = 0; i< 5; i++)
    {
        if(num_in_backup >= BACKUP_MAX)
            break;
        QString pid = QString::number(random(1000, 9999));
        //qDebug()<<pid;
        int prio = random(10, 20);
        int runt = random(5, 10);
        int memory = random(2,8);
        PCB * p =new PCB(pid, prio, runt, memory);
        qDebug() << "随机生成：" << p->getPID();
        if(backupque->addBackup(p))
        {
            num_in_backup++;
        }
        else break;//后备队列已满
        qDebug() << num_in_backup;
    }
    backupque->refreshAgain();
}

PCB* SchedulePage::popReadyProcs()//弹出就绪队列中第一个进程
{
    if(num_in_memory > 0)
    {
        PCB* p = new PCB(ready[0]);
        delete  ready[0];
        if(ready[0] != nullptr)
        {
            ready[0] = nullptr;
        }

        for(int i=0;i<num_in_memory;i++){//覆盖ready[0]
            ready[i] = ready[i+1];
        }
        num_in_memory--;
        updateReadyQueData();
        initProcsInMemory();
        return p;
    }
    else return nullptr;
}

/**
 * @brief SchedulePage::exitMemoryRelease
 * @param leaveProcs
 * @return 1/0
 * 进程出内存后的内存释放，修改未分分区表内容和内存代表数组的内容，刷新未分分区表展示表格
 */
int SchedulePage::exitMemoryRelease(PCB *leaveProcs)
{
    int begin = leaveProcs->getMemoryBegin();
    int usage = leaveProcs->getMemoryNeed();
    unpartitionTable.append(new UnpartitionTable(begin, usage, NOTPARTIED));
    for(int i=begin;i<begin+usage;i++){//置空代表内存的相应空间
        memory[i] = 0;
    }
    refreshUnpartiTable();//刷新未分分区表
}

/**
 * @brief SchedulePage::exitMemoryMerge
 * 合并函数，在进程出主存的时候进行调用，合并内存碎片
 */
void SchedulePage::exitMemoryMerge()
{
    unpartitionTableSort();//按照起址排序
    int size = unpartitionTable.size();
    for(int i=0;i<unpartitionTable.size() - 1;i++){
        if(i>=unpartitionTable.size()-1)   break;//防止越界
        int beginAddr = unpartitionTable.at(i)->beginAddr;
        int width = unpartitionTable.at(i)->width;
        if(unpartitionTable.at(i+1)->beginAddr==(beginAddr+width) ){
            unpartitionTable.at(i)->width += unpartitionTable.at(i+1)->width;
            UnpartitionTable *t = unpartitionTable.at(i+1);
            unpartitionTable.removeAt(i+1);
            delete t;
            t = NULL;
        }
    }
    refreshProcsColors();
    refreshMemoryTable();
    refreshUnpartiTable();
}

/**
 * @brief SchedulePage::unpartitionTableSort
 * 对未分分区表的各项按照起址进行小到大的排序
 */
void SchedulePage::unpartitionTableSort()//对未分分区表的各项按照起址进行小到大的排序
{
    qSort(unpartitionTable.begin(), unpartitionTable.end(), cmpOfUnptTableSort);
}

bool SchedulePage::cmpOfUnptTableSort(const UnpartitionTable *info1, const UnpartitionTable *info2)
{
    return info1->beginAddr < info2->beginAddr;//从小到大，按起址排序
}

void SchedulePage::on_backupQueButton_clicked()
{
    backupque->show();
    backupque->refreshAgain();
}

void SchedulePage::on_addWorkButton_clicked()
{
    addwork->show();
}

void SchedulePage::on_random_clicked()//随机生成5个进程
{
    srand((unsigned)time(NULL));//PID随机数种子init
   randomProcess();
//    for(int i=0;i<10;i++){
//        QSqlDatabase db = getDB();
//        bool ok = db.open();

//        if(ok){
//            QString pid = QString::number(random(1000, 9999));
//            QString intprio = QString::number(random(10,100));
//            QString intrunt = QString::number(random(10,20));
//            QString sql = QString("INSERT INTO backupque(pid, status, priority, runtime)"
//                                  " VALUES('%1', 0, %2, %3)").arg(pid).arg(intprio).arg(intrunt);
//            QSqlQuery insert(db);
//            bool success = insert.exec(sql);
//            if(success)
//            {
//                qDebug()<< "插入成功";
//            }
//            else
//            {
//                QMessageBox::information(nullptr, "database connect error", "数据库连接失败");
//                return;
//            }
//        }
//        else {
//           qDebug()<< "Database open failed in on_Random_clicked()";
//           return;
//        }
//    }
}

void SchedulePage::on_terminateButton_clicked()//终止按钮
{
    ifCpuScheduleIsRunning = false;
    stopTimers();
    updateCpuStatus();
}

void SchedulePage::on_readyQue_customContextMenuRequested(const QPoint &pos)//挂起
{
    on_terminateButton_clicked();
    QMenu menu;
    QAction * lock = new QAction(tr("挂起"));
    menu.addAction(lock);
    connect(lock, &QAction::triggered, [=]{
        QModelIndex index = ui->readyQue->indexAt(pos);//找到当前位置信息
        int selProcess = index.row(); //获取到了当前右键所选的行数
        if(index.isValid()) //如果行数有效
        {
            qDebug() << "选中了第" << index.row() << "行";
            suspend[num_in_suspend++] = new PCB(ready[selProcess]);
            qDebug() << suspend[num_in_suspend - 1]->getPID();

            //挂起进程退出主存
            exitMemoryRelease(ready[selProcess]);
            exitMemoryMerge();

            delete ready[selProcess];
            num_in_memory--;
            if(ready[selProcess] != nullptr)
            {
                ready[selProcess] = nullptr;
            }
            for(int i = selProcess; i < num_in_memory; i++)//覆盖被挂起的进程
            {
                ready[i] = ready[i + 1];
            }
            ready[num_in_memory] = nullptr;
            updateReadyQueData();
            updateSusbendData();
        }
        else {
            qDebug() << "获取的行数无效";
        }
            on_continueButton_clicked();
    });
    menu.exec(QCursor::pos());
}

void SchedulePage::on_suspendque_customContextMenuRequested(const QPoint &pos)//解挂
{
    on_terminateButton_clicked();

    QMenu menu;
    QAction * unlock = new QAction(tr("解挂"));
    menu.addAction(unlock);
    connect(unlock, &QAction::triggered, [=]{
        QModelIndex index = ui->suspendque->indexAt(pos);//找到当前位置信息
        int selProcess = index.row(); //获取到了当前右键所选的行数
        if(index.isValid()) //如果行数有效
        {
            qDebug() << "选中了第" << index.row() << "行";
            this->unlock[num_unlock++] = new PCB(suspend[selProcess]);
            //qDebug() << suspend[num_in_suspend - 1]->getPID();

            delete suspend[selProcess];
            num_in_suspend--;
            if(suspend[selProcess] != nullptr)
            {
                suspend[selProcess] = nullptr;
            }
            for(int i = selProcess; i < num_in_suspend; i++)//覆盖被挂起的进程
            {
                suspend[i] = suspend[i + 1];
            }
            suspend[num_in_memory] = nullptr;
            updateSusbendData();
        }
        else {
            qDebug() << "获取的行数无效";
        }
            on_continueButton_clicked();
    });
    menu.exec(QCursor::pos());
}

void SchedulePage::initTimers()
{
    unit_time = startTimer(1000);
    time_slice = startTimer(5203);
}

void SchedulePage::stopTimers()
{
    this->killTimer(unit_time);
    this->killTimer(time_slice);
}

void SchedulePage::on_continueButton_clicked()
{
    ifCpuScheduleIsRunning = true;
    initTimers();
    updateCpuStatus();
}

void SchedulePage::resizeEvent(QResizeEvent*)
{
    ui->bg->resize(this->width(),this->height());
    ui->bg->setScaledContents(true);
}

/**
 * @brief SchedulePage::getIndexByFirst
 * @param procsWidth
 * @return 最先适应
 */
int SchedulePage::getIndexByFirst(int procsWidth)
{
    unpartitionTableSort();//先按照起址排序，从小到大
    for(int i=0;i<unpartitionTable.size();i++){
        if(unpartitionTable.at(i)->width>=procsWidth){
            return i;
        }
    }
    return -1;
}

/**
 * @brief SchedulePage::getIndexByBest
 * @param procsWidth
 * @return 最优适应
 */
int SchedulePage::getIndexByBest(int procsWidth)
{
    unpartitionTableSortBySize();//先按照空间排序，从小到大
    for(int i=0;i<unpartitionTable.size();i++){
        if(unpartitionTable.at(i)->width>=procsWidth){
            return i;
        }
    }
    return -1;
}

/**
 * @brief SchedulePage::getIndexByWorst
 * @param procsWidth
 * @return 最差适应
 */
int SchedulePage::getIndexByWorst(int procsWidth)
{
    unpartitionTableSortBySize();//先按照空间排序，从小到大
    if(unpartitionTable.at(unpartitionTable.size()-1)->width>=procsWidth)
        return unpartitionTable.size()-1;
    else
        return -1;
}

/**
 * @brief SchedulePage::unpartitionTableSortBySize
 * 按照空间排序，从小到大
 */
void SchedulePage::unpartitionTableSortBySize()
{
    qSort(unpartitionTable.begin(),unpartitionTable.end(),cmpOfUnptTableSortBySize);
}


bool SchedulePage::cmpOfUnptTableSortBySize(const UnpartitionTable *info1, const UnpartitionTable *info2)
{
    return info1->width < info2->width;//按照空间大小排序
}

/**
 * @brief SchedulePage::getCurMemoryPercent
 * 更新当前的内存占比
 */
void SchedulePage::getCurMemoryPercent()
{
    float sum = MEMORYSIZE - sumOfUnptTableSpace();
    int result = (sum/MEMORYSIZE)*100;
//    qDebug()<<result;
    curmemorypercent = result;
}

void SchedulePage::on_addrassignpatterns_currentIndexChanged(int index)
{
    addrAssign = index;
}
